<cfcomponent name="HibernateFactory" hint="Factory for getting to Hibernate objects">

<cfscript>
	instance = StructNew();
</cfscript>

<cffunction name="newHashMap">
	<cfreturn createObject("java", "java.util.HashMap").init() />
</cffunction>

<cffunction name="newHashSet">
	<cfargument name="firstItem" required="false">
	<cfset var hashset = createObject("java", "java.util.HashSet").init() />
	<cfif structKeyExists(arguments,"firstItem")>
		<cfset hashset.add(firstItem) />
	</cfif>
	<cfreturn hashset />
</cffunction>

<cffunction name="javaDate">
	<cfargument name="indate">
	<cfset var daDate = createODBCDateTime(indate) />
	<cfreturn createObject("java", "java.util.HashSet").init(daDate.getTime()) />
</cffunction>

<cffunction name="invokeObjectMethod">
	<cfargument name="inObject" required="true" />
	<cfargument name="methodName" required="true" />
	<cfargument name="methodArguments" default="#structNew()#" />
	<cfset var methodResult = "" />
	<cfinvoke component="#arguments.inObject#" method="#arguments.methodName#" returnvariable="methodResult" argumentcollection="#arguments.methodArguments#">
		<!--- <cfinvokeargument name="value"> --->
	</cfinvoke>
	<cfif isDefined('methodResult')>
		<cfreturn methodResult />
	</cfif>
</cffunction>

<cffunction name="dumpvar">
  <cfargument name="var" default="blank">
  <cfargument name="abort" default="true">
  <cftry>
	  <cfdump var="#var#">
	<cfcatch>
	  <cfset writeoutput(cfcatch.Message & " " & cfcatch.Detail & " " & cfcatch.TagContext[1].line & " " & cfcatch.stacktrace) />
	</cfcatch>
	</cftry>
	<cfif arguments.abort>
	  <cfabort />
	</cfif>
</cffunction>

<cffunction name="throwerror">
  <cfargument name="type" required="true" />
  <cfargument name="message" required="true" />
	<cfthrow detail="#arguments.message#" type="#arguments.type#" />
</cffunction>

<!------------------------------------------- PUBLIC ------------------------------------------->

<cffunction name="init" hint="Constructor: takes directory to export to" access="public" returntype="EntityAdapter" output="false">
	<cfargument name="entitiesMetaData">	
	<cfscript>
	 	instance.entitiesMetaData = arguments.entitiesMetaData;
	 	return this;
	</cfscript>
</cffunction>


<cffunction name="getObjectTransferObject" hint="Constructor: takes directory to export to" access="public" returntype="any" output="false">
	<cfargument name="inobject" required="true">	
	<cfargument name="propertyList" required="true">
	<cfscript>
		var object = arguments.inobject;
		var objectMetaData = getMetaData(object);
		var transferObject = structNew();
		var getfunc = "";
		var funcResults = "";
		var funcName = "";
		var funcNames = "";
		var funcAccess = "";
		var f = "";
		if(structKeyExists(objectMetaData,"functions")) {
			for(f = 1; f lte arrayLen(objectMetaData["functions"]); f = f+1) {
			funcName = replace(objectMetaData["functions"][f]["name"],"get","");
			funcAccess = objectMetaData["functions"][f]["access"];
				if(listFindNoCase("public,remote",funcAccess)) {
				funcNames = listAppend(funcNames,funcName);
				}
			}
			if(listFindNoCase(funcNames,"_getTO")) {
				return object._getTO();
			}
			for(f = 1; f lte listLen(funcNames); f = f+1) {
				funcName = listGetAt(funcNames,f);
				if(listFindNoCase(arguments.propertyList,funcName)) {
					//getfunc = object["get" & funcName];
					funcResults = invokeObjectMethod(object,"get#funcName#");
					transferObject[funcName] = funcResults;
				}
			}
		}
//		dumpvar(transferObject);
	 	return transferObject;
	</cfscript>
</cffunction>

<cffunction name="setObjectTransferObject" hint="Constructor: takes directory to export to" access="public" returntype="any" output="false">
	<cfargument name="inobject" required="true">	
	<cfargument name="propertyList" required="true">
	<cfargument name="valuestruct" required="true">
	<cfscript>
		var object = arguments.inobject;
		var objectMetaData = getMetaData(object);
		var transferObject = structNew();
		var getfunc = "";
		var funcResults = "";
		var funcArgs = "";
		var funcName = "";
		var funcNames = "";
		var funcAccess = "";
		var f = "";
		if(structKeyExists(objectMetaData,"functions")) {
			for(f = 1; f lte arrayLen(objectMetaData["functions"]); f = f+1) {
			funcName = replace(objectMetaData["functions"][f]["name"],"set","");
			funcAccess = objectMetaData["functions"][f]["access"];
				if(listFindNoCase("public,remote",funcAccess)) {
				funcNames = listAppend(funcNames,funcName);
				}
			}
			if(listFindNoCase(funcNames,"_setTO")) {
				return object._setTO(valuestruct);
			}
			for(f = 1; f lte listLen(funcNames); f = f+1) {
				funcName = listGetAt(funcNames,f);
				if(listFindNoCase(arguments.propertyList,funcName)) {
					//getfunc = object["get" & funcName];
					funcArgs = structNew();
					funcArgs[funcName] = valuestruct[funcName];
					funcResults = invokeObjectMethod(object,"set#funcName#",funcArgs);
				}
			}
		}
//		dumpvar(transferObject);
	 	return inobject;
	</cfscript>
</cffunction>

<cffunction name="setComponentTransferObject" hint="Constructor: takes directory to export to" access="public" returntype="any" output="false">
	<cfargument name="inobject" required="true">	
	<cfargument name="propertyList" required="true">
	<cfargument name="valuestruct" required="true">
	<cfscript>
		var object = arguments.inobject;
		var objectMetaData = getMetaData(object);
		var transferObject = structNew();
		var getfunc = "";
		var funcResults = "";
		var funcArgs = "";
		var funcName = "";
		var funcNames = "";
		var funcAccess = "";
		var f = "";
		if(structKeyExists(objectMetaData,"functions")) {
			for(f = 1; f lte arrayLen(objectMetaData["functions"]); f = f+1) {
			funcName = replace(objectMetaData["functions"][f]["name"],"set","");
			funcAccess = objectMetaData["functions"][f]["access"];
				if(listFindNoCase("public,remote",funcAccess)) {
				funcNames = listAppend(funcNames,funcName);
				}
			}
			if(listFindNoCase(funcNames,"_setTO")) {
				return object._setTO(valuestruct);
			}
			for(f = 1; f lte listLen(funcNames); f = f+1) {
				funcName = listGetAt(funcNames,f);
				if(listFindNoCase(arguments.propertyList,funcName)) {
					//getfunc = object["get" & funcName];
					funcArgs = structNew();
					funcArgs[funcName] = valuestruct[funcName];
					funcResults = invokeObjectMethod(object,"set#funcName#",funcArgs);
				}
			}
		}
//		dumpvar(transferObject);
	 	return inobject;
	</cfscript>
</cffunction>

<cffunction name="convertToHashmap" hint="Constructor: takes directory to export to" access="public" returntype="any" output="false">
	<cfargument name="entityName" required="true">	
	<cfargument name="object" required="true">	
	<cfscript>
		var entityProperties = instance.entitiesMetaData[arguments.entityName]["properties"];
		var propertyList = structKeyList(entityProperties);
		var propertyName = "";
		var transferObject = getObjectTransferObject(arguments.object,propertyList);
		var hashmap = newHashMap();
		var p = 0;
		hashmap.put("$type$",entityName);
		
 	  for(p = 1; p lte listLen(propertyList); p = p+1) {
 	  	propertyName = listGetAt(propertyList,p);
 	  	setProperty(hashmap,entityProperties[propertyName],transferObject);
 	  }
//		dumpvar(hashmap);
	 	return hashmap;
	</cfscript>
</cffunction>

<cffunction name="setProperty" hint="Constructor: takes directory to export to" access="public" returntype="any" output="false">
	<cfargument name="inhashmap" required="true">	
	<cfargument name="propertyMetaData" required="true">	
	<cfargument name="transferObject" required="true">	
	<cfscript>
		var hashmap = arguments.inhashmap;
		var propertyName = propertyMetaData["propertyname"];
		var propertyColumn = propertyMetaData["column"];
		var propertyType = propertyMetaData["type"].getName();
		var classname = propertyMetaData["type"].getClass().getName();
		var propertyNullable = propertyColumn["nullable"];
		var newSet = "";
		var componentHashmap = "";
//				dumpvar(propertyMetaData);
		//TODO: fix null issue, whack
		try {
			if (NOT propertyNullable AND NOT isStruct(transferObject[propertyName]) AND structKeyList(getMetaData(transferObject[propertyName])) neq ""
					AND transferObject[propertyName] eq "") {
				try {entityName = propertyMetaData["type"].getAssociatedEntityName();} catch( Any Excpt) { entityName = ""; }
				throwerror("cfhibernate.property.required.error",entityName & " - Set Property Error: Property '" & propertyName & "' is required but is absent");
			}
		} 
		catch (any excpt) {
			//dumpvar(transferObject[propertyName]);
			throwerror("hibernate.setproperty.error","something bad happened");
		}
		//if(NOT isStruct(transferObject[propertyName]) AND NOT transferObject[propertyName] eq "") {}
		if (propertyType eq "date") {
			hashmap.put(propertyName,javaDate(transferObject[propertyName]));
		} 
		if (propertyType eq "datetime") {
			hashmap.put(propertyName,javaDate(transferObject[propertyName]));
		} 
		else if (propertyType eq "string") {
			hashmap.put(propertyName,javacast("string",transferObject[propertyName]));
		}
		else if (propertyType eq "integer") {
			hashmap.put(propertyName,javacast("int",transferObject[propertyName]));
		} else {
			//theoretically, everything else is an entity?
			dynamicMetaData = getMetaData(transferObject[propertyName]);
			// native hibernate object returns empty structkeylist-- maybe a hack, but works
			if(structKeyList(dynamicMetaData) eq "") {
				hashmap.put(propertyName,transferObject[propertyName]);
			}
			// many to many
			else if (find("java.util.Set",propertyType)) {
				//newSet = newHashSet(convertToHashMap(propertyType,transferObject[propertyName]));
				hashmap.put(propertyName,transferObject[propertyName]);				
			} 
			// cf component, needs conversion
			else if (structKeyExists(dynamicMetaData,"extends")) {
				componentHashmap = convertToHashMap(propertyType,transferObject[propertyName]);
				//dumpvar(componentHashmap);
				//newSet = newHashSet(componentHashmap);
				//hashmap.put(propertyName,newSet);
				hashmap.put(propertyName,componentHashmap);
			} 
			// many to one / one to one
			else {
				newSet = newHashSet(convertToHashMap(propertyType,transferObject[propertyName]));
				hashmap.put(propertyName,newSet);
			}
			if(listFirst(propertyType,".") neq "jndiexample") {
				dumpvar("Unknown property type:" & propertyType,false);
				dumpvar(transferObject,false);
				//dumpvar(propertyMetaData);
			}
		}
	</cfscript>
</cffunction>


</cfcomponent>
